<template>
  <Page style="display: flex; justify-content: center; align-items: center; height: 100vh; position: relative">
    <!-- 游戏画布 -->
    <canvas class="canvasBody"></canvas>

    <!-- 游戏状态面板 -->
    <div
      style="
        position: absolute;
        top: 10px;
        left: 10px;
        font-family: Arial;
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
        color: white;
        background-color: rgba(0, 0, 0, 0.7);
        padding: 10px;
        border-radius: 8px;
        z-index: 10;
      "
    >
      <p>
        玩家:
        <el-input v-if="editName" v-model="playerName" @keyup.enter="setName" style="width: 100px"></el-input>
        <span v-else class="font-bold" @click="clickNameSpan(Date.now())">{{ playerName || playerId }}</span>
      </p>
      <p>
        状态:
        <span class="font-bold">{{ gameStatus }}</span>
      </p>
      <p>
        延迟:
        <span class="font-bold" :style="getColor(latency)">{{ latency }}ms</span>
      </p>
      <p>
        分数:
        <span class="font-bold text-yellow-400">{{ score }}</span>
      </p>
      <p>
        生命值:
        <span class="font-bold text-red-500">{{ health }}</span>
      </p>
    </div>

    <!-- 聊天与音视频面板 -->
    <div
      style="
        position: absolute;
        top: 10px;
        right: 10px;
        width: 400px;
        height: 600px;
        font-family: Arial;
        color: white;
        background-color: rgba(255, 255, 255, 1);
        padding: 10px;
        border-radius: 8px;
        display: flex;
        flex-direction: column;
        z-index: 10;
      "
    >
      <!-- 音视频聊天组件 -->
      <VoiceChat
        ref="voiceChatRef"
        @sendMediaBuffer="handleSendMediaBuffer"
        @sendMediaData="handleSendMediaData"
        style="margin-bottom: 10px"
      />

      <!-- 聊天选择器 -->
      <el-select
        v-model="currentChatTarget"
        @change="switchChat"
        placeholder="选择聊天对象"
        style="margin-bottom: 5px; color: #333"
      >
        <el-option label="房间" :value="ROOM_ID"></el-option>
        <el-option
          v-for="item in Object.values(players).filter(p => p.id !== playerId)"
          :key="item.id"
          :label="item.playerName || item.id"
          :value="item.id"
        ></el-option>
      </el-select>

      <!-- 聊天消息区域 -->
      <div class="chat-messages" style="flex: 1; overflow-y: auto; color: #333">
        <p
          v-for="(message, index) in chatRecords[currentChatTarget]"
          :key="index"
          :class="{
            'self-message': message.sender.id === playerId,
            'other-message': message.sender.id !== playerId
          }"
        >
          <img
            :src="getAvatarUrl(message.sender.id)"
            alt="Avatar"
            style="width: 20px; height: 20px; margin-right: 5px"
          />
          <span class="font-bold" v-if="message.sender.id !== playerId">{{ getPersonName(message.sender.id) }}:</span>
          {{ message.text }}
          <span class="font-bold" v-if="message.sender.id === playerId">:{{ getPersonName(message.sender.id) }}</span>
        </p>
      </div>

      <!-- 消息输入区域 -->
      <div style="display: flex; gap: 5px; margin-top: 5px">
        <el-input v-model="chatInput" @keyup.enter="sendChatMessage" placeholder="输入消息" style="flex: 1"></el-input>
        <el-button @click="sendChatMessage">发送</el-button>
      </div>
    </div>
  </Page>
</template>

<script setup>
import Page from '@/components/page/index.vue'
import VoiceChat from './components/VoiceChat.vue' // 引入音视频组件
import { ref, onMounted, onBeforeUnmount, reactive, watch, computed } from 'vue'

// 游戏核心状态
const canvasRef = ref(null)
const ctx = ref(null)
const ws = ref(null)
const playerId = ref('')
const playerName = ref('')
const editName = ref(false)
const lastClickTime = ref(0)
const gameStatus = ref('连接中...')
const players = ref({})
const bullets = ref([])
const obstacles = ref([])
const score = ref(0)
const health = ref(3)
const gameWidth = 1200
const gameHeight = 600
const tankSize = 40
const bulletSize = 10
const gridInterval = 60

// 网络与性能监控
const latency = ref(0)
const lastPingTime = ref(0)

// 音视频相关状态
const voiceChatRef = ref(null)
const currentVoiceTarget = ref(null)
const currentChatTarget = ref('room') // 默认房间聊天

// 按键与输入状态
const pressedKeys = reactive({
  up: false,
  down: false,
  left: false,
  right: false
})
const longPressTimers = reactive({
  up: null,
  down: null,
  left: null,
  right: null
})
const LONG_PRESS_DELAY = 200
const LONG_PRESS_INTERVAL = 50

// 聊天系统
const ROOM_ID = 'room'
const chatRecords = ref({ [ROOM_ID]: [] })
const chatInput = ref('')

// 工具函数：节流与防抖
const throttle = function (fn, time) {
  let timer = 0
  return function (...args) {
    const nowDate = Date.now()
    if (nowDate - timer >= time) {
      fn.apply(this, args)
      timer = nowDate
    }
  }
}

const debounce = function (fn, time) {
  let timer
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, time)
  }
}

// 玩家名称编辑
function clickNameSpan(time) {
  if (time - lastClickTime.value < 300) {
    editName.value = true
  }
  lastClickTime.value = time
}

function setName() {
  sendMessage({ type: 'setName', data: { playerName: playerName.value } })
  editName.value = false
}

// 延迟颜色指示
function getColor(num) {
  if (num < 100) return 'color: green'
  if (num > 300) return 'color: red'
  return 'color: yellow'
}

// 聊天系统
function sendChatMessage() {
  if (!chatInput.value.trim()) return
  const isPrivate = currentChatTarget.value !== ROOM_ID
  const message = {
    type: 'chat',
    data: {
      sender: { name: playerName.value, id: playerId.value },
      text: chatInput.value,
      isPrivate,
      target: isPrivate ? currentChatTarget.value : ROOM_ID
    }
  }
  sendMessage(message)
  chatInput.value = ''
}

function getPersonName(playerId) {
  const player = players.value[playerId]
  return player?.playerName || player?.id?.slice(0, 6) || '未知玩家'
}

function getAvatarUrl(playerId) {
  return `https://api.dicebear.com/6.x/identicon/svg?seed=${playerId}`
}

// 切换聊天对象时自动建立音视频连接
function switchChat() {
  if (!chatRecords.value[currentChatTarget.value]) {
    chatRecords.value[currentChatTarget.value] = []
  }

  // 切换到玩家时自动发起通话（非房间且非当前通话对象）
  if (currentChatTarget.value !== ROOM_ID && currentChatTarget.value !== currentVoiceTarget.value) {
    startMediaCall(currentChatTarget.value)
  }
}

// 音视频通话控制
function startMediaCall(targetId) {
  if (targetId === playerId.value) {
    gameStatus.value = '不能与自己通话'
    return
  }
  if (!voiceChatRef.value) {
    console.error('音视频组件未加载')
    return
  }

  currentVoiceTarget.value = targetId
  voiceChatRef.value.startMediaCall(targetId)
  gameStatus.value = `正在与 ${getPersonName(targetId)} 通话`
}

// 二进制媒体数据封装与发送（关键修复）
function handleSendMediaBuffer(buffer) {
  if (!ws.value || ws.value.readyState !== WebSocket.OPEN) {
    console.warn('WebSocket未连接，无法发送媒体数据')
    return
  }

  try {
    ws.value.send(buffer)
  } catch (error) {
    console.error('媒体数据封装失败:', error)
  }
}

// JSON状态数据发送
function handleSendMediaData(message) {
  if (message.data?.targetId) {
    sendMessage(message)
  } else {
    console.warn('状态数据缺少目标ID:', message)
  }
}

// WebSocket初始化
function initWebSocket() {
  playerId.value = Math.random().toString(36).substr(2, 9)
  try {
    ws.value = new WebSocket(`wss://10.41.0.20:8080/websocket/${playerId.value}`)

    ws.value.onopen = () => {
      gameStatus.value = '已连接，等待开始...'
    }

    ws.value.onmessage = async event => {
      // 区分二进制媒体数据和JSON文本数据
      if (event.data instanceof Blob) {
        await handleBinaryMessage(event.data)
      } else {
        try {
          const data = JSON.parse(event.data)
          handleMessage(data)
        } catch (e) {
          console.error('解析JSON消息失败:', e)
        }
      }
    }

    ws.value.onclose = event => {
      gameStatus.value = `连接断开 (${event.code})`
      console.log('WebSocket关闭:', event)
    }

    ws.value.onerror = error => {
      gameStatus.value = '连接错误'
      console.error('WebSocket错误:', error)
    }
  } catch (e) {
    gameStatus.value = '连接失败'
    console.error('WebSocket初始化失败:', e)
  }
}

// 2. 解包函数
function unpackData(buffer) {
  const view = new DataView(buffer)

  // 读取类型
  const type = view.getUint8(0)

  // 读取targetId长度
  const targetIdLength = view.getUint8(1)

  // 读取targetId
  const targetIdBytes = new Uint8Array(buffer, 2, targetIdLength)
  const targetId = new TextDecoder().decode(targetIdBytes)

  // 读取数据
  const dataOffset = 2 + targetIdLength
  const dataLength = buffer.byteLength - dataOffset
  const data = buffer.slice(dataOffset)

  return { type, targetId, data }
}

// 二进制媒体数据解析（对应服务端格式）
async function handleBinaryMessage(blob) {
  try {
    const arrayBuffer = await blob.arrayBuffer()
    const arrayData = unpackData(arrayBuffer)

    if (voiceChatRef.value) {
      voiceChatRef.value.handleMediaData(arrayData)
    }
  } catch (error) {
    console.error('解析二进制消息失败:', error)
  }
}

// 服务器文本消息处理
function handleMessage(data) {
  switch (data.type) {
    case 'gameState':
      if (data.timestamp < lastPingTime.value) return
      throttleUpdateLast(Date.now(), lastPingTime.value)
      lastPingTime.value = data.timestamp
      updatePlayers(data.players)
      bullets.value = data.bullets || []
      obstacles.value = data.obstacles || []
      gameStatus.value = data.status || '游戏中'
      drawGame()
      updateScoreAndHealth()
      break

    case 'connectionSuccess':
      playerId.value = data.data.playerId
      gameStatus.value = '连接成功'
      break

    case 'playerDisconnect':
      const leavingPlayer = getPersonName(data.data.playerId)
      gameStatus.value = `${leavingPlayer} 已离开`
      // 若断开的是当前通话对象，结束通话
      if (data.data.playerId === currentVoiceTarget.value) {
        voiceChatRef.value?.endMediaCall()
        currentVoiceTarget.value = null
      }
      break

    case 'error':
      gameStatus.value = `错误: ${data.message}`
      break

    case 'chat':
      const target = data.data.isPrivate ? data.data.target : ROOM_ID
      if (!chatRecords.value[target]) chatRecords.value[target] = []
      chatRecords.value[target].push(data.data)
      break

    // 音视频状态消息处理
    case 'startCall':
      if (data.data.targetId === playerId.value) {
        // 收到通话请求时自动接听（或显示弹窗确认）
        startMediaCall(data.senderId)
      }
      break

    case 'endCall':
      currentVoiceTarget.value = null
    case 'videoStatus':
    case 'audioStatus':
      voiceChatRef.value?.handleStatusData(data)
      break

    default:
      console.warn('未知消息类型:', data.type)
  }
}

// 游戏逻辑辅助函数
function updatePlayers(serverPlayers) {
  players.value = { ...serverPlayers }
}

function updateScoreAndHealth() {
  const player = players.value[playerId.value]
  if (player) {
    score.value = player.score || 0
    health.value = player.health || 3
  }
}

const throttleUpdateLast = throttle((timestamp, lastTime) => {
  if (lastPingTime.value) latency.value = timestamp - lastTime
}, 1000)

// 键盘控制
function handleKeyDown(e) {
  if (e.repeat) return
  switch (e.key) {
    case 'ArrowUp':
      if (!pressedKeys.up) {
        pressedKeys.up = true
        sendMove('up')
        startLongPressTimer('up')
      }
      break
    case 'ArrowDown':
      if (!pressedKeys.down) {
        pressedKeys.down = true
        sendMove('down')
        startLongPressTimer('down')
      }
      break
    case 'ArrowLeft':
      if (!pressedKeys.left) {
        pressedKeys.left = true
        sendMove('left')
        startLongPressTimer('left')
      }
      break
    case 'ArrowRight':
      if (!pressedKeys.right) {
        pressedKeys.right = true
        sendMove('right')
        startLongPressTimer('right')
      }
      break
    case ' ':
      sendMessage({ type: 'shoot' })
      break
  }
}

function handleKeyUp(e) {
  switch (e.key) {
    case 'ArrowUp':
      pressedKeys.up = false
      clearLongPressTimer('up')
      break
    case 'ArrowDown':
      pressedKeys.down = false
      clearLongPressTimer('down')
      break
    case 'ArrowLeft':
      pressedKeys.left = false
      clearLongPressTimer('left')
      break
    case 'ArrowRight':
      pressedKeys.right = false
      clearLongPressTimer('right')
      break
  }
}

function startLongPressTimer(direction) {
  longPressTimers[direction] = setTimeout(() => {
    longPressTimers[direction] = setInterval(() => {
      if (pressedKeys[direction]) {
        sendMove(direction)
      }
    }, LONG_PRESS_INTERVAL)
  }, LONG_PRESS_DELAY)
}

function clearLongPressTimer(direction) {
  if (longPressTimers[direction]) {
    clearTimeout(longPressTimers[direction])
    clearInterval(longPressTimers[direction])
    longPressTimers[direction] = null
  }
}

function sendMove(direction) {
  const player = players.value[playerId.value]
  if (!player) return

  player.angle = getAngleFromDirection(direction)
  sendMessage({
    type: 'move',
    data: { direction, timestamp: Date.now() }
  })
}

function getAngleFromDirection(direction) {
  const angles = {
    up: -Math.PI / 2,
    down: Math.PI / 2,
    left: Math.PI,
    right: 0
  }
  return angles[direction]
}

// 画布绘制函数
function initCanvas() {
  const canvas = document.querySelector('.canvasBody')
  if (!canvas) {
    console.error(' 无法获取 canvas 元素 ')
    return
  }
  canvas.width = gameWidth
  canvas.height = gameHeight
  ctx.value = canvas.getContext('2d')
}

function drawGame() {
  if (!ctx.value) return
  ctx.value.clearRect(0, 0, gameWidth, gameHeight)
  drawGrid()
  drawObstacles()
  drawBullets()
  drawPlayers()
}

function drawGrid() {
  ctx.value.strokeStyle = '#444'
  ctx.value.lineWidth = 0.8
  for (let x = 0; x < gameWidth; x += gridInterval) {
    ctx.value.beginPath()
    ctx.value.moveTo(x, 0)
    ctx.value.lineTo(x, gameHeight)
    ctx.value.stroke()
  }
  for (let y = 0; y < gameHeight; y += gridInterval) {
    ctx.value.beginPath()
    ctx.value.moveTo(0, y)
    ctx.value.lineTo(gameWidth, y)
    ctx.value.stroke()
  }
}

function drawObstacles() {
  obstacles.value.forEach(obs => {
    ctx.value.fillStyle = '#555'
    ctx.value.fillRect(obs.x, obs.y, obs.width, obs.height)
    ctx.value.strokeStyle = '#888'
    ctx.value.strokeRect(obs.x, obs.y, obs.width, obs.height)
  })
}

function drawBullets() {
  bullets.value.forEach(bullet => {
    ctx.value.fillStyle = '#ffcc00'
    ctx.value.beginPath()
    ctx.value.arc(bullet.x, bullet.y, bulletSize, 0, Math.PI * 2)
    ctx.value.fill()
  })
}

function drawPlayers() {
  Object.values(players.value).forEach(player => {
    ctx.value.save()
    ctx.value.translate(player.x + tankSize / 2, player.y + tankSize / 2)
    ctx.value.rotate(player.angle)

    // 本地玩家特殊颜色
    ctx.value.fillStyle = player.id === playerId.value ? '#4CAF50' : '#ff3300'
    ctx.value.fillRect(-tankSize / 2, -tankSize / 2, tankSize, tankSize)

    // 炮管
    ctx.value.fillStyle = '#333'
    ctx.value.fillRect(0, -5, tankSize / 2 + 10, 10)
    ctx.value.restore()

    // 生命值条
    ctx.value.fillStyle = player.health > 1 ? 'green' : 'red'
    ctx.value.fillRect(player.x, player.y - 10, tankSize * (player.health / 3), 5)

    // 玩家名称
    ctx.value.font = '14px Arial'
    ctx.value.textAlign = 'center'
    ctx.value.fillStyle = 'white'
    ctx.value.fillText(player.playerName || player.id.slice(0, 6), player.x + tankSize / 2, player.y - 20)
  })
}

// 消息发送通用函数
function sendMessage(message) {
  if (ws.value?.readyState === WebSocket.OPEN) {
    ws.value.send(JSON.stringify(message))
  } else {
    console.warn('WebSocket 未连接，无法发送消息 ')
  }
}

// 生命周期管理
onMounted(() => {
  initCanvas()
  initWebSocket()
  window.addEventListener('keydown', handleKeyDown)
  window.addEventListener('keyup', handleKeyUp)
  drawGame()
})

onBeforeUnmount(() => {
  // 清理 WebSocket
  if (ws.value) {
    ws.value.close(1000, ' 客户端关闭 ')
  }

  // 移除事件监听
  window.removeEventListener('keydown', handleKeyDown)
  window.removeEventListener('keyup', handleKeyUp)

  // 结束通话
  voiceChatRef.value?.endMediaCall()
})
</script>

<style scoped>
.canvasBody {
  border: 1px solid black;
  background-color: #1a1a1a;
  image-rendering: pixelated;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
  border-radius: 8px;
}

.font-bold {
  font-weight: bold;
}

.text-yellow-400 {
  color: #fbbf24;
}

.text-red-500 {
  color: #ef4444;
}

.chat-messages {
  background-color: #f5f5f5;
  padding: 10px;
  border-radius: 4px;
}

.self-message {
  text-align: right;
  margin: 5px 0;
  padding: 5px;
  background-color: #e3f2fd;
  border-radius: 4px;
}

.other-message {
  text-align: left;
  margin: 5px 0;
  padding: 5px;
  background-color: #ffebee;
  border-radius: 4px;
}
</style>